/**
 * 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2，另有两个整数 m 和 n ，分别表示 nums1 和 nums2 中的元素数目。
 * 请你 合并 nums2 到 nums1 中，使合并后的数组同样按 非递减顺序 排列。
 * 注意：最终，合并后数组不应由函数返回，而是存储在数组 nums1 中。
 * 为了应对这种情况，nums1 的初始长度为 m + n，其中前 m 个元素表示应合并的元素，后 n 个元素为 0 ，应忽略。
 * nums2 的长度为 n 。
 * 
 * 示例 1：
    输入：nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
    输出：[1,2,2,3,5,6]
    解释：需要合并 [1,2,3] 和 [2,5,6] 。
    合并结果是 [1,2,2,3,5,6] ，其中斜体加粗标注的为 nums1 中的元素。
 * 示例 2：
    输入：nums1 = [1], m = 1, nums2 = [], n = 0
    输出：[1]
    解释：需要合并 [1] 和 [] 。
    合并结果是 [1] 。
 * 示例 3：
    输入：nums1 = [0], m = 0, nums2 = [1], n = 1
    输出：[1]
    解释：需要合并的数组是 [] 和 [1] 。
    合并结果是 [1] 。
    注意，因为 m = 0 ，所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
 */

// 直接合并后排序
// 时间复杂度：O((m + n) * log(m + n))
// 空间复杂度：O(log(m + n))
const merge = function (nums1, m, nums2, n) {
    nums1.splice(m, nums1.length - m, ...nums2);
    nums1.sort((a, b) => a - b);
};

// 双指针
// 时间复杂度：O(m + n)
// 空间复杂度：O(m + n)
const merge2 = function (nums1, m, nums2, n) {
    let p1 = 0, p2 = 0; // 初始化两个双指针，分别指向num1和num2的第一个元素
    const sorted = new Array(m + n).fill(0); // 创建一个新数组，用于存储排序后的结果
    let cur; // 定义临时变量
    while (p1 < m || p2 < n) {
        if (p1 === m) {
            // p1 === m，说明num1已经遍历完，此时将nums2[p2]存入cur变量中，p2指向下一个元素
            cur = nums2[p2++];
        } else if (p2 === n) {
            // p2 === n，说明num2已经遍历完，此时将nums1[p1]存入cur变量中，p1指向下一个元素
            cur = nums1[p1++];
        } else if (nums1[p1] < nums2[p2]) {
            // p1指向的元素小于p2指向的元素，则将p1指向的元素存入cur变量中，p1指向下一个元素
            cur = nums1[p1++];
        } else {
            // 否则，将p2指向的元素存入cur变量中，p2指向下一个元素
            cur = nums2[p2++];
        }
        sorted[p1 + p2 - 1] = cur; // 将cur存入结果数组中，经过上面的判断，p1和p2只有一个会加1，所以是p1 + p2 - 1
    }
    for (let i = 0; i != m + n; ++i) {
        nums1[i] = sorted[i]; // 就地改变nums1
    }
}

// 逆向双指针
// 时间复杂度：O(m + n)
// 空间复杂度：O(1)
/**
 * 方法二用了一个临时变量保存结果，如何不用临时变量，而是直接插入到nums1中呢？
 * 观察可知，nums1的后半部分，即从 m 到 m + n - 1的部分，都是空的，可以直接覆盖不影响结果
 * 每次取最大的，放到nums1的最后面
 * 但是后半部分能足够容纳被插入的元素吗？
 * 考虑某个时刻，nums1数组有 m - p1 - 1 个元素被放入 num1 的后半部分，nums2数组有 n - p2 - 1 个元素被放入 num1 的后半部分
 * 而在指针p1的后面，nums1数组有 m + n - p1 - 1 个位置
 * 由于 m + n - p1 - 1 >= m - p1 - 1 + n - p2 - 1
 * 等价于 p2 >= -1 永远成立，p1后面的位置永远足够容纳被插入的元素，不会产生p1的元素被覆盖的情况
 */
const merge3 = function (nums1, m, nums2, n) {
    let p1 = m - 1, p2 = n - 1; // 从后面开始遍历，找最大的
    let tail = m + n - 1; // 指向nums1的最后一个元素
    let cur; // 存放当前的最大值
    while (p1 >= 0 || p2 >= 0) { // 只要nums1或nums2没有遍历完
        if (p1 === -1) {
            // nums1遍历完了
            cur = nums2[p2--]; // 取nums2中p2指向的元素，p2向前移动一位
        } else if (p2 === -1) {
            // nums2遍历完了
            cur = nums1[p1--]; // 取nums1中p1指向的元素，p1向前移动一位
        } else if (nums1[p1] > nums2[p2]) {
            cur = nums1[p1--]; // nums1中p1指向的元素大于nums2中p2指向的元素，取大者，p1向前移动一位
        } else {
            cur = nums2[p2--]; // 否则，取nums2中p2指向的元素，p2向前移动一位
        }
        nums1[tail--] = cur; // 将cur赋值到nums1中tail指向的位置，tail向前移动一位
    }
}
